// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package scanner

import (
	"fmt"
	"go/token"
	"io"
	"sort"
)

// 在错误列表中，错误由*错误表示。
// 位置Pos，如果有效，则指向
// 违规标记的开头，错误条件由消息
// 描述。
// 
type Error struct {
	Pos token.Position
	Msg string
}

// 错误实现错误接口。
func (e Error) Error() string {
	if e.Pos.Filename != "" || e.Pos.IsValid() {
		// 不要打印“<unknown position>”
		// TODO（gri）重新考虑position的语义。IsValid 
		return e.Pos.String() + ": " + e.Msg
	}
	return e.Msg
}

// ErrorList是*个错误的列表。
// ErrorList的零值是一个空的ErrorList，可以使用。
// 
type ErrorList []*Error

// Add将给定位置的错误和错误消息添加到错误列表中。
func (p *ErrorList) Add(pos token.Position, msg string) {
	*p = append(*p, &Error{pos, msg})
}

// 重置将错误列表重置为无错误。
func (p *ErrorList) Reset() { *p = (*p)[0:0] }

// ErrorList实现排序接口。注意，仅仅比较文件偏移量是不够的，因为偏移量不反映修改后的行信息（通过注释）。
func (p ErrorList) Len() int      { return len(p) }
func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func (p ErrorList) Less(i, j int) bool {
	e := &p[i].Pos
	f := &p[j].Pos
	if e.Filename != f.Filename {
		return e.Filename < f.Filename
	}
	if e.Line != f.Line {
		return e.Line < f.Line
	}
	if e.Column != f.Column {
		return e.Column < f.Column
	}
	return p[i].Msg < p[j].Msg
}

// 排序对错误列表进行排序*错误条目按位置排序，
// 其他错误按错误消息排序，在任何*错误之前
// 条目。
// 
func (p ErrorList) Sort() {
	sort.Sort(p)
}

// RemoveMultiples对错误列表进行排序，并删除每行中除第一个错误以外的所有错误。
func (p *ErrorList) RemoveMultiples() {
	sort.Sort(p)
	var last token.Position // 首末。线路是！=任何法律错误行
	i := 0
	for _, e := range *p {
		if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
			last = e.Pos
			(*p)[i] = e
			i++
		}
	}
	*p = (*p)[0:i]
}

// 错误列表实现错误接口。
func (p ErrorList) Error() string {
	switch len(p) {
	case 0:
		return "no errors"
	case 1:
		return p[0].Error()
	}
	return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
}

// Err返回一个与此错误列表等效的错误。
// 如果列表为空，Err返回nil。
func (p ErrorList) Err() error {
	if len(p) == 0 {
		return nil
	}
	return p
}

// PrintError是一个实用函数，如果err参数是ErrorList，它会将错误列表打印到w，
// 每行一个错误。否则
// 它将打印错误字符串。
// 
func PrintError(w io.Writer, err error) {
	if list, ok := err.(ErrorList); ok {
		for _, e := range list {
			fmt.Fprintf(w, "%s\n", e)
		}
	} else if err != nil {
		fmt.Fprintf(w, "%s\n", err)
	}
}
